package com.jack.lib.net

import com.blankj.utilcode.util.EncryptUtils
import com.hjq.gson.factory.GsonFactory
import com.jack.lib.core.ext.sumByStr
import com.jack.lib.net.cache.NetCache
import com.jack.lib.net.gson.JGsonConverterFactory
import com.jack.lib.net.interceptor.JHeaderInterceptor
import com.jack.lib.net.interceptor.JLoggingInterceptor
import com.jack.lib.net.interceptor.JResponseBodyInterceptor
import com.jack.lib.net.verifier.JHostnameVerifier
import com.jakewharton.retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
import com.jeremy.retrofitmock.SimpleMockInterceptor
import kotlinx.coroutines.job
import okhttp3.OkHttpClient
import retrofit2.Retrofit
import java.lang.reflect.Method
import java.lang.reflect.Proxy
import java.util.concurrent.TimeUnit
import kotlin.coroutines.Continuation

/**
 * author  : jack(黄冲)
 * time    : 2020/7/28 16:36
 * desc    :
 */
object JNetManager {

    const val TAG = "jhttp"

    private var retrofit: Retrofit? = null

    fun <T: Any> createService(service: Class<T>, inet: INet): T {
        val retrofit = retrofit?: genericRetrofit(inet)
        return proxyService(service, retrofit.create(service))
    }

    private fun genericRetrofit(inet: INet): Retrofit {
        return Retrofit.Builder()
            .client(genericOkHttpClient(inet))
            .baseUrl(inet.baseUrl())
            .addConverterFactory(JGsonConverterFactory.create(GsonFactory.getSingletonGson()))
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
            .build()
    }

    private fun genericOkHttpClient(inet: INet): OkHttpClient {
        return OkHttpClient().newBuilder()
            .hostnameVerifier(JHostnameVerifier())
            .addInterceptor(JLoggingInterceptor())
            .addInterceptor(JHeaderInterceptor(inet))
            .addInterceptor(SimpleMockInterceptor(inet.enableMock()))
            .addInterceptor(JResponseBodyInterceptor())
            .connectTimeout(inet.connectTimeout(), TimeUnit.MILLISECONDS)
            .readTimeout(inet.readTimeout(), TimeUnit.MILLISECONDS)
            .writeTimeout(inet.writeTimeout(), TimeUnit.MILLISECONDS)
            .build()
    }

    private fun <T> proxyService(apiService: Class<T>, apiServiceInstance: Any): T {
        return Proxy.newProxyInstance(this::class.java.classLoader, arrayOf(apiService)) { o: Any?, method: Method?, objects: Array<Any?>? ->
            val key: String
            val invoke = if (objects == null) {
                key = EncryptUtils.encryptMD5ToString(method?.name)
                method?.invoke(apiServiceInstance)
            } else {
                key = EncryptUtils.encryptMD5ToString(objects.map { it.toString() }.sumByStr { it })
                method?.invoke(apiServiceInstance, *objects)
            }
            if (objects?.lastOrNull() is Continuation<*>) {
                val continuation = objects.lastOrNull() as Continuation<*>
                val children = continuation.context.job.children
                val hashCode = continuation.hashCode()
            }
            if (key.isNotEmpty() && invoke != null) {
                NetCache.put(key,  invoke.hashCode(), method?.name.orEmpty())
            }
            invoke
        } as T
    }

}